package main

import (
	"fmt"
	"sync"
)

/*
defer是Go语言中的延迟执行语句，用来添加函数结束时执行的代码，常用于释放资源和数据连接、关闭文件、释放锁、捕获panic等。
defer后面一般跟函数或方法，Go语言机制担保一定会执行defer语句中的代码
一、defer触发时机
Go官方文档中对defer的执行时机做了阐述，分别是：
包裹defer的函数返回时
包裹defer的函数执行到末尾时
所在的goroutine发生panic时
二、defer执行逻辑
1、 多个defer语句按先进后出的方式执行(多个defer调用顺序是先进后出，defer后的操作可以理解为压入栈中。)
2、defer声明时，对应的参数会实时解析(defer后面跟方法、有参函数、无参函数。)
3、defer、return、返回值三者的执行逻辑
return最先执行，return负责将结果写入返回值中；接着defer开始执行一些收尾工作；最后函数携带当前返回值（可能和最初的返回值不相同）退出。
当defer语句放在return后面时，就不会被执行。
return 的实现逻辑：
	1、第一步给返回值赋值(若是有名返回值直接赋值，匿名返回值则先声明再赋值);
	2、第二步调用RET返回指令并传入返回值，RET会检查是否存在defer语句，若存在就先逆序插播defer语句;
	3、最后RET携带返回值退出函数 。
可以看出，return不是一个原子操作，函数返回值与RET返回值并不一定一致。
defer在return之后执行，但在函数退出之前，defer可以修改返回值。
*/

func manyDfer() {
	fmt.Println("我是函数主体逻辑")
	defer fmt.Println("我是第一个定义的defer")
	defer fmt.Println("我是第二个定义的defer")
	defer fmt.Println("我是第三个定义的defer")
}

func testDefer(a int) (ret int) {
	defer func() {
		ret += 1
	}() //defer的是个匿名函数的执行
	return a
}

func main() {
	/*
		一、defer使用(把要延迟执行的语句或函数放在它后面就可以)
	*/
	// 在java语言中，当我们使用锁，或者连接数据库，打开文件时，在最后需要关闭锁，关闭连接等，但是由于中间有许多逻辑，
	//会导致打开和关闭的语句离得很远，有可能会导致忘记关闭，造成问题，所有Go语言中引入了defer延迟执行关键字
	var mu sync.Mutex //声明锁

	mu.Lock()         //使用锁
	defer mu.Unlock() //解锁，在defer的语句会放在函数最后执行，这样就不会忘记，下面可以写正常的程序逻辑
	/*
		二、多个defer的执行顺序
	*/
	// 调用多个defer函数
	manyDfer()
	/*
		调用结果如下，可以看到先定义defer后执行，所有defer类似栈(先入后出)
		我是函数主体逻辑
		我是第三个定义的defer
		我是第二个定义的defer
		我是第一个定义的defer
	*/

	/*
		三、defer return 返回值三者的关系
	*/
	//当函数中有defer return 返回值三个的时候
	result := testDefer(10)
	fmt.Println(result) //结果11,按道理返回值应该是10，
	// 从结果我们可以看到，函数return先给返回值赋值10，然后检查是否有defer，然后执行defer，此时的ret=10 然后执行匿名函数，最后执行返回值11
}
